#!/usr/bin/env python3

from datetime import datetime
import datetime as dt
import backtrader as bt
import utils
from termcolor import colored
from config import DEVELOPMENT, COIN_TARGET, COIN_REFER, ENV, PRODUCTION, DEBUG
from backtrader import num2date
from utils import send_telegram_message
import pytz


class StrategyBase( bt.Strategy ):
    def __init__(self):
        self.order = None
        self.last_operation = "SELL"
        self.status = "DISCONNECTED"
        self.bar_executed = 0
        self.buy_price_close = None
        self.soft_sell = False
        self.hard_sell = False
        self.log( "Base strategy initialized" )

    def reset_sell_indicators(self):
        self.soft_sell = False
        self.hard_sell = False
        self.buy_price_close = None

    def notify_data(self, data, status, *args, **kwargs):
        self.status = data._getstatusname( status )
        print( self.status )
        if status == data.LIVE:
            self.log( "LIVE DATA - Ready to trade" )

    def short(self):
        utils.notify_signal( "SELL", num2date( self.data0.datetime[0], pytz.timezone( 'Asia/Shanghai' ) ),
                             self.data0.close[0] )

        if self.last_operation == "SELL":
            return

        if ENV == DEVELOPMENT:
            self.log( "Sell ordered: $%.2f" % self.data0.close[0] )
            return self.sell()

        cash, value = self.broker.get_wallet_balance( COIN_TARGET )
        amount = value * 0.99
        self.log( "Sell ordered: $%.2f. Amount %.6f %s - $%.2f USDT" % (self.data0.close[0],
                                                                        amount, COIN_TARGET, value), True )
        return self.sell( size=amount )

    def long(self):
        utils.notify_signal( "BUY", num2date( self.data0.datetime[0], pytz.timezone( 'Asia/Shanghai' ) ),
                             self.data0.close[0] )

        if self.last_operation == "BUY":
            return

        self.log( "Buy ordered: $%.2f" % self.data0.close[0], True )
        self.buy_price_close = self.data0.close[0]
        price = self.data0.close[0]

        if ENV == DEVELOPMENT:
            return self.buy()

        cash, value = self.broker.get_wallet_balance( COIN_REFER )
        amount = (value / price) * 0.99  # Workaround to avoid precision issues
        self.log( "Buy ordered: $%.2f. Amount %.6f %s. Ballance $%.2f USDT" % (self.data0.close[0],
                                                                               amount, COIN_TARGET, value), True )
        return self.buy( size=amount, valid=datetime.utcnow() )

    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            self.log( 'ORDER ACCEPTED/SUBMITTED' )
            self.order = order
            return

        if order.status in [order.Expired]:
            self.log( 'BUY EXPIRED', True )

        elif order.status in [order.Completed]:
            if order.isbuy():
                self.last_operation = "BUY"
                self.log(
                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm), True )
                if ENV == PRODUCTION:
                    print( order.__dict__ )

            else:  # Sell
                self.last_operation = "SELL"
                self.reset_sell_indicators()
                self.log( 'SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                          (order.executed.price,
                           order.executed.value,
                           order.executed.comm), True )

            # Sentinel to None: new orders allowed
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log( 'Order Canceled/Margin/Rejected: Status %s - %s' % (order.Status[order.status],
                                                                          self.last_operation), True )

        self.order = None

    def notify_trade(self, trade):
        if not trade.isclosed:
            return

        color = 'green'
        if trade.pnl < 0:
            color = 'red'

        self.log( colored( 'OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm), color ), True )

    def log(self, txt, send_telegram=False, color=None):
        if not DEBUG:
            return

        value = datetime.now()

        if len( self ) > 0:
            value = self.data0.datetime.datetime() + dt.timedelta( hours=8 )

        if color:
            txt = colored( txt, color )

        print( '[%s] %s' % (value.strftime( "%y-%m-%d %H:%M" ), txt) )
        if send_telegram:
            send_telegram_message( txt )
